/* * Bitronix Transaction Manager * * Copyright (c) 2010, Bitronix Software. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package bitronix.tm.mock; import bitronix.tm.BitronixTransactionManager; import bitronix.tm.TransactionManagerServices; import bitronix.tm.internal.BitronixXAException; import bitronix.tm.mock.events.EventRecorder; import bitronix.tm.mock.events.JournalLogEvent; import bitronix.tm.mock.events.XAResourceEndEvent; import bitronix.tm.mock.events.XAResourceRollbackEvent; import bitronix.tm.mock.events.XAResourceStartEvent; import bitronix.tm.mock.resource.MockJournal; import bitronix.tm.mock.resource.MockXAResource; import bitronix.tm.mock.resource.jdbc.MockitoXADataSource; import bitronix.tm.resource.jdbc.JdbcConnectionHandle; import bitronix.tm.resource.jdbc.PoolingDataSource; import junit.framework.TestCase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.sql.XAConnection; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.xa.XAException; import javax.transaction.xa.XAResource; import java.lang.reflect.Field; import java.lang.reflect.Proxy; import java.sql.Connection; import java.util.List; public class DelistmentTest extends TestCase { private final static Logger log = LoggerFactory.getLogger(DelistmentTest.class); private PoolingDataSource poolingDataSource1; private PoolingDataSource poolingDataSource2; private BitronixTransactionManager btm; protected void setUp() throws Exception { EventRecorder.clear(); // change disk journal into mock journal Field field = TransactionManagerServices.class.getDeclaredField("journal"); field.setAccessible(true); field.set(TransactionManagerServices.class, new MockJournal()); poolingDataSource1 = new PoolingDataSource(); poolingDataSource1.setClassName(MockitoXADataSource.class.getName()); poolingDataSource1.setUniqueName("pds1"); poolingDataSource1.setMinPoolSize(5); poolingDataSource1.setMaxPoolSize(5); poolingDataSource1.setAutomaticEnlistingEnabled(true); poolingDataSource1.init(); poolingDataSource2 = new PoolingDataSource(); poolingDataSource2.setClassName(MockitoXADataSource.class.getName()); poolingDataSource2.setUniqueName("pds2"); poolingDataSource2.setMinPoolSize(5); poolingDataSource2.setMaxPoolSize(5); poolingDataSource2.setAutomaticEnlistingEnabled(true); poolingDataSource2.init(); btm = TransactionManagerServices.getTransactionManager(); } protected void tearDown() throws Exception { poolingDataSource1.close(); poolingDataSource2.close(); btm.shutdown(); } public void testDelistErrorOnCommit() throws Exception { btm.begin(); Connection connection1 = poolingDataSource1.getConnection(); JdbcConnectionHandle handle1 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection1); XAConnection xaConnection1 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle1.getPooledConnection()); MockXAResource xaResource1 = (MockXAResource) xaConnection1.getXAResource(); connection1.createStatement(); // triggers enlistment xaResource1.setEndException(new BitronixXAException("screw delistment", XAException.XAER_RMERR)); xaResource1.setRollbackException(new BitronixXAException("delistment was screwed, cannot rollback", XAException.XAER_RMERR)); Connection connection2 = poolingDataSource2.getConnection(); JdbcConnectionHandle handle2 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection2); XAConnection xaConnection2 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle2.getPooledConnection()); MockXAResource xaResource2 = (MockXAResource) xaConnection2.getXAResource(); connection2.createStatement(); // triggers enlistment try { btm.commit(); fail("expected RollbackException"); } catch (RollbackException ex) { assertEquals("delistment error caused transaction rollback" + System.getProperty("line.separator") + " resource(s) [pds1] could not be delisted", ex.getMessage()); } // check flow List orderedEvents = EventRecorder.getOrderedEvents(); log.info(EventRecorder.dumpToString()); assertEquals(9, orderedEvents.size()); int i=0; assertEquals(Status.STATUS_ACTIVE, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_MARKED_ROLLBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_ROLLING_BACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertTrue(((XAResourceRollbackEvent) orderedEvents.get(i++)).getSource() == xaResource2); assertEquals(Status.STATUS_ROLLEDBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); } public void testDelistUnilateralRollbackOnCommit() throws Exception { btm.begin(); Connection connection1 = poolingDataSource1.getConnection(); JdbcConnectionHandle handle1 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection1); XAConnection xaConnection1 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle1.getPooledConnection()); MockXAResource xaResource1 = (MockXAResource) xaConnection1.getXAResource(); connection1.createStatement(); xaResource1.setEndException(new BitronixXAException("what was that transaction again?", XAException.XAER_NOTA)); xaResource1.setRollbackException(new BitronixXAException("delistment unilaterally rolled back, cannot rollback twice", XAException.XAER_RMERR)); Connection connection2 = poolingDataSource2.getConnection(); JdbcConnectionHandle handle2 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection2); XAConnection xaConnection2 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle2.getPooledConnection()); MockXAResource xaResource2 = (MockXAResource) xaConnection2.getXAResource(); connection2.createStatement(); // triggers enlistment try { btm.commit(); fail("expected RollbackException"); } catch (RollbackException ex) { assertEquals("delistment error caused transaction rollback" + System.getProperty("line.separator") + " resource(s) [pds1] unilaterally rolled back", ex.getMessage()); } // check flow List orderedEvents = EventRecorder.getOrderedEvents(); log.info(EventRecorder.dumpToString()); assertEquals(9, orderedEvents.size()); int i=0; assertEquals(Status.STATUS_ACTIVE, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_MARKED_ROLLBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_ROLLING_BACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertTrue(((XAResourceRollbackEvent) orderedEvents.get(i++)).getSource() == xaResource2); assertEquals(Status.STATUS_ROLLEDBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); } public void testDelistErrorAndUnilateralRollbackOnCommit() throws Exception { btm.begin(); Connection connection1 = poolingDataSource1.getConnection(); JdbcConnectionHandle handle1 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection1); XAConnection xaConnection1 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle1.getPooledConnection()); MockXAResource xaResource1 = (MockXAResource) xaConnection1.getXAResource(); xaResource1.setEndException(new BitronixXAException("screw delistment", XAException.XAER_RMERR)); xaResource1.setRollbackException(new BitronixXAException("delistment was screwed, cannot rollback", XAException.XAER_RMERR)); connection1.createStatement(); // triggers enlistment Connection connection2 = poolingDataSource2.getConnection(); JdbcConnectionHandle handle2 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection2); XAConnection xaConnection2 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle2.getPooledConnection()); MockXAResource xaResource2 = (MockXAResource) xaConnection2.getXAResource(); xaResource2.setEndException(new BitronixXAException("what was that transaction again?", XAException.XAER_NOTA)); xaResource2.setRollbackException(new BitronixXAException("delistment unilaterally rolled back, cannot rollback twice", XAException.XAER_RMERR)); connection2.createStatement(); // triggers enlistment try { btm.commit(); fail("expected RollbackException"); } catch (RollbackException ex) { assertEquals("delistment error caused transaction rollback" + System.getProperty("line.separator") + " resource(s) [pds2] unilaterally rolled back" + System.getProperty("line.separator") + " resource(s) [pds1] could not be delisted" , ex.getMessage()); } // check flow List orderedEvents = EventRecorder.getOrderedEvents(); log.info(EventRecorder.dumpToString()); assertEquals(8, orderedEvents.size()); int i=0; assertEquals(Status.STATUS_ACTIVE, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_MARKED_ROLLBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_ROLLING_BACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(Status.STATUS_ROLLEDBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); } public void testDelistErrorAndUnilateralRollbackOnRollback() throws Exception { btm.begin(); Connection connection1 = poolingDataSource1.getConnection(); JdbcConnectionHandle handle = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection1); XAConnection xaConnection1 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle.getPooledConnection()); MockXAResource xaResource1 = (MockXAResource) xaConnection1.getXAResource(); xaResource1.setEndException(new BitronixXAException("screw delistment", XAException.XAER_RMERR)); xaResource1.setRollbackException(new BitronixXAException("delistment was screwed, cannot rollback", XAException.XAER_RMERR)); connection1.createStatement(); // triggers enlistment Connection connection2 = poolingDataSource2.getConnection(); JdbcConnectionHandle handle2 = (JdbcConnectionHandle) Proxy.getInvocationHandler(connection2); XAConnection xaConnection2 = (XAConnection) AbstractMockJdbcTest.getWrappedXAConnectionOf(handle2.getPooledConnection()); MockXAResource xaResource2 = (MockXAResource) xaConnection2.getXAResource(); xaResource2.setEndException(new BitronixXAException("what was that transaction again?", XAException.XAER_NOTA)); xaResource2.setRollbackException(new BitronixXAException("delistment unilaterally rolled back, cannot rollback twice", XAException.XAER_RMERR)); connection2.createStatement(); // triggers enlistment btm.rollback(); log.info(EventRecorder.dumpToString()); // check flow List orderedEvents = EventRecorder.getOrderedEvents(); log.info(EventRecorder.dumpToString()); assertEquals(8, orderedEvents.size()); int i=0; assertEquals(Status.STATUS_ACTIVE, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMNOFLAGS, ((XAResourceStartEvent) orderedEvents.get(i++)).getFlag()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_MARKED_ROLLBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(XAResource.TMSUCCESS, ((XAResourceEndEvent) orderedEvents.get(i++)).getFlag()); assertEquals(Status.STATUS_ROLLING_BACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); assertEquals(Status.STATUS_ROLLEDBACK, ((JournalLogEvent) orderedEvents.get(i++)).getStatus()); } }